﻿using Scrat.Core.Common;
using Scrat.Core.Pojo;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using static System.Windows.Forms.AxHost;

namespace Scrat.Core.Server
{
   
    /// <summary>
    /// UDP 消息服务
    /// </summary>
    public class UDPMessageServer
    {
        // 消息的前5位为标识为自己应用发送的消息
        public static readonly byte[] OwnAppTag = new byte[] { 1, 1, 1, 1 };

        private static UDPMessageServer _instance = null;


        private UdpClient _udpClient;
        private IPEndPoint _udpEndPoint;
        public List<UdpClient> Clients { get; private set; }

        public bool RunningStatus { get; set; }

        public struct UdpState
        {
            public UdpClient client;
            public IPEndPoint endPoint;

            public UdpState(UdpClient client, IPEndPoint endPoint)
            {
                this.client = client;
                this.endPoint = endPoint;
            }
        }

        private UDPMessageServer()
        { 
            Clients = new List<UdpClient>();
        }

        public static UDPMessageServer GetInstance()
        {
            if (_instance == null)
            { 
                _instance = new UDPMessageServer();
            }
            return _instance;
        }

        public void Start(string address, int port)
        { 
            // try udpClient init 
            try
            {
                _udpEndPoint = new IPEndPoint(IPAddress.Parse(address), port);
                _udpClient = new UdpClient(_udpEndPoint);

                StartReceiveAsync();

                RunningStatus = true;
                if (ServerStartSuccess != null)
                {
                    ServerStartSuccess();
                }
            }
            catch(Exception ex)
            {
                RunningStatus = false;
                if (ServerErrorEvent != null)
                {
                    ServerErrorEvent(ex.Message.ToString());
                }
                Console.WriteLine(ex.StackTrace.ToString());
            }
        }

        public void RestartServer(string address, int port)
        {
            try
            {
                Stop();
                Start(address, port);
            } catch (Exception ex)
            {
                Console.WriteLine(ex.StackTrace.ToString());
            }
            
        }

        /// <summary>
        /// 返回值为发送的字节数
        /// </summary>
        /// <param name="datagram"></param>
        /// <param name="address"></param>
        /// <param name="port"></param>
        /// <returns></returns>
        public async Task SendDataGram(Datagram datagram)
        {
            if (RunningStatus)
            {
                byte[] objData = GeneralTool.Serialize<Datagram>(datagram);
                // 将发送的数据添加标记 标记 + 数据
                byte[] data = GeneralTool.MergeBytes(OwnAppTag, objData);
                await _udpClient.SendAsync(data, data.Length,
                        new IPEndPoint(datagram.RemoteIPAddr, datagram.RemotePort))
                    .ContinueWith((result) => 
                    {
                        if (DataSendSuccess != null)
                        {
                            DataSendSuccess(datagram);
                        }
                    });
            } else
            {
                if (DataSendError != null)
                {
                    DataSendError("服务未启动! 请更正参数并重新启动哦~");
                }
            }
        }

        public delegate void ServerStartSuccessDelegate();
        public event ServerStartSuccessDelegate ServerStartSuccess;

        public delegate void DataSendErrorDelegate(string errorMessage);
        public event DataSendErrorDelegate DataSendError;

        public delegate void DataSendSuccessDelegate(Datagram datagram);
        public event DataSendSuccessDelegate DataSendSuccess;


        public delegate void ServerErrorDelegate(string errorMessage);
        public event ServerErrorDelegate ServerErrorEvent;

        public delegate void DataReceiveDelegate(Datagram datagram);
        public event DataReceiveDelegate DataReceiveEvent;

        private void StartReceiveAsync()
        {
            UdpState state = new UdpState(_udpClient, _udpEndPoint);
            try
            {
                _udpClient.BeginReceive(new AsyncCallback(OnDatareceive), state);
            }
            catch (Exception ex)
            {
                if (ServerErrorEvent != null)
                {
                    ServerErrorEvent(ex.Message.ToString());
                }
                Console.WriteLine(ex.StackTrace.ToString());
            }
            
        }

        private void OnDatareceive(IAsyncResult ar)
        {
            bool ownApp = true;
            if (ar.IsCompleted)
            {
                UdpState state = (UdpState)ar.AsyncState;
                UdpClient client = state.client;
                Datagram datagram;
                try
                {
                    byte[] objByte = client.EndReceive(ar, ref state.endPoint);

                    if (objByte != null && objByte.Length > 4)
                    {
                        List<byte[]> resultByte = GeneralTool.SplitByte(objByte, 4);
                        for (int i = 0; i < 4; i++)
                        {
                            if (resultByte[0][i] != OwnAppTag[i])
                            {
                                ownApp = false;
                            }
                        }

                        if (ownApp)
                        {
                            datagram = GeneralTool.Deserialize<Datagram>(resultByte[1]);
                        }
                        else
                        {
                            // 外部数据报
                            datagram = new Datagram(Identity.Unknown, DataGramType.OuterData, objByte);
                        }
                    }
                    else
                    {
                        // 外部数据报
                        datagram = new Datagram(Identity.Unknown, DataGramType.OuterData, objByte);
                    }


                    var endPoint = state.endPoint;
                    datagram.RemoteIPAddr = endPoint.Address;
                    datagram.RemotePort = endPoint.Port;

                    // 发布接收到数据事件
                    if (DataReceiveEvent != null)
                    {
                        DataReceiveEvent(datagram);
                    }

                    
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.StackTrace.ToString());
                }
                finally
                {
                    if (RunningStatus)
                    {
                        try
                        {
                            client?.BeginReceive(new AsyncCallback(OnDatareceive), state);
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine(ex.StackTrace.ToString());
                        }
                    }
                }
               
                
            }
        }

        public void Stop()
        { 
            if (!RunningStatus) 
            {
                return;
            }
            RunningStatus = false;
            try
            {
                _udpClient.Close();
                _udpClient.Dispose();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.StackTrace.ToString());
            }
            finally
            {
                _udpClient = null;
            }
        }
    }
}
